/*  chatd - queue.c
*   Copyright (C) 2006  Michael Bruno
*
*   chatd is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.
*
*   chatd is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with chatd; if not, write to the Free Software
*   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include "xboard_io.h"

/* Enqueues the msg onto the queue q
 * Returns 1 on failure, 0 on success
 */
int enqueue(queue *q, xboard_cmd_t cmd)
{
	list *new;

	if (q->max)
#ifndef __MACH__
		sem_wait(&(q->empty));	/* Decrease the empty count */
#else
		semaphore_wait(q->empty);
#endif

	pthread_mutex_lock(&(q->lock));

	if (!(new = (list *) malloc(sizeof(list))))
		/* Out of memory */
		return 1;

	new->next = NULL;  /* this is the last element in queue */
	new->cmd = cmd;

	if (q->size > 0)
		/* if queue is not empty, make the previously last element
		   point to this new one as the next element */
		q->back->next = new;
	else
		/* however if the queue is empty, make the
		   front of the queue point to this new element */
		q->front = new;

	/* and this element becomes the back */
	q->back = new;

#ifndef __MACH__
	if (sem_post(&(q->full))) /* increase the full count */
		/* reached an internal maximum count */
		return 1;
#else
	if (semaphore_signal(q->full) != KERN_SUCCESS)
		return 1;
#endif

	q->size++;
	pthread_mutex_unlock(&(q->lock));

	return 0;
}

/* Dequeues the front msg of the queue q into msg
 * Returns 1 on failure, 0 on success
 */
int dequeue(queue *q, xboard_cmd_t *cmd)
{
	list *tmp;

#ifndef __MACH__
	sem_wait(&(q->full));  /* Decrease the full count */
#else
	semaphore_wait(q->full);
#endif

	pthread_mutex_lock(&(q->lock));

	*cmd = q->front->cmd;	/* return the front element's data */

	tmp = q->front;
	q->front = tmp->next;	/* the next element becomes the front */
	free(tmp);		/* and free the old front element */

	if (q->max)
#ifndef __MACH__
		if (sem_post(&(q->empty))) /* increase the empty count */
			/* reached an internal maximum count */
			return 1;
#else
		if (semaphore_signal(q->empty) != KERN_SUCCESS)
			return 1;
#endif

	q->size--;
	pthread_mutex_unlock(&(q->lock));

	return 0;
}

/* Returns a new empty queue structure with a maximum size of max.
 * If max is < 1 then there is no maximum size.
 */
queue *create_queue(int max)
{
	queue *q;

	if (!(q = (queue *) malloc(sizeof(queue))))
		/* out of memory */
                return NULL;

	q->front = NULL;
	q->back = NULL;
	q->size = 0;

	pthread_mutex_init(&(q->lock), NULL); /* always returns 0 */

#ifndef __MACH__
	if (sem_init(&(q->full), 0, 0))
		return NULL;

	if (sem_init(&(q->empty), 0, max > -1 ? max : 0))
		return NULL;
#else
	if (semaphore_create(mach_task_self(), &(q->full), SYNC_POLICY_FIFO,
			     0) != KERN_SUCCESS)
		return NULL;

	if (semaphore_create(mach_task_self(), &(q->empty), SYNC_POLICY_FIFO,
			     max > -1 ? max : 0) != KERN_SUCCESS)
		return NULL;
#endif


	q->max = max > 0;

	return q;
}

/* Frees the queue q. Assumes that it is empty
 * Returns 1 on failure, 0 on success
 */
int free_queue(queue *q)
{
#ifndef __MACH__
	if (	pthread_mutex_destroy(&(q->lock)) ||
		sem_destroy(&(q->full)) ||
		sem_destroy(&(q->empty))) return 1;
#else
	if (pthread_mutex_destroy(&(q->lock)))
		return 1;

	if (semaphore_destroy(mach_task_self(), q->full) != KERN_SUCCESS &&
	    semaphore_destroy(mach_task_self(), q->empty) != KERN_SUCCESS)
		return 1;
#endif

	free(q);

	return 0;
}

